package com.gym.core.common;

import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLException;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.UnknownHostException;

/**
 *
 * yibin.du
 * HttpUtil based httpcomponet 4.3.1
 * support https and pooled httpconnection.
 */
public class HttpUtil {
    private static final Logger logger = LoggerFactory.getLogger(HttpUtil.class);

    private static final int CON_TIMEOUT = 5000;
    private static final int SO_TIMEOUT = 20000;
    private static final int POOL_TIMECOUT = 3000;
    private static final int HTTP_RETRY_COUNT = 3;
    private static final String NOTICELINE = "--------------------------------------------";
    private static CloseableHttpClient httpclient;


    static {
        init();
    }


    public static void init() throws RuntimeException {
        try {
            logger.warn(NOTICELINE + " httpUtil init begin " + NOTICELINE);
            SSLContextBuilder sslContextBuilder = new SSLContextBuilder();
            sslContextBuilder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
            SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContextBuilder.build(), SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

            Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create().
                    register("http", new PlainConnectionSocketFactory()).
                    register("https", sslConnectionSocketFactory).
                    build();

            logger.warn(NOTICELINE + " SSL context init done " + NOTICELINE);

            PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(registry);
            poolingHttpClientConnectionManager.setMaxTotal(30);
            poolingHttpClientConnectionManager.setDefaultMaxPerRoute(3);
            RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(POOL_TIMECOUT).setConnectTimeout(CON_TIMEOUT).setSocketTimeout(SO_TIMEOUT).build();
            HttpClientBuilder httpClientBuilder = HttpClients.custom();
            httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);
            httpClientBuilder.setDefaultRequestConfig(requestConfig);
            httpClientBuilder.setRetryHandler(new HttpRequestRetryHandler() {
                public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
                    if (executionCount >= HTTP_RETRY_COUNT) {
                        return false;
                    }
                    if (exception instanceof InterruptedIOException) {
                        logger.warn("httputil4 retry for InterruptIOException");
                        return true;
                    }
                    if (exception instanceof UnknownHostException) {
                        // Unknown host
                        return false;
                    }
                    if (exception instanceof ConnectTimeoutException) {
                        logger.warn("httputil4 retry for ConnectTimeoutException");
                        return true;
                    }
                    if (exception instanceof SSLException) {
                        return false;
                    }
                    HttpClientContext clientContext = HttpClientContext.adapt(context);
                    HttpRequest request = clientContext.getRequest();
                    boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
                    if (idempotent) {
                        // Retry if the request is considered idempotent
                        logger.warn("httputil4 retry for idempotent");
                        return true;
                    }
                    return false;
                }
            });

            logger.warn(NOTICELINE + " poolManager , requestconfig init done " + NOTICELINE);
            httpclient = httpClientBuilder.build();
            logger.warn(NOTICELINE + " httpUtil4 init done " + NOTICELINE);
        } catch (Exception e) {
            logger.error(NOTICELINE + "httpclient init fail" + NOTICELINE, e);
            throw new RuntimeException(e);
        }
    }

    public static CloseableHttpClient getHttpclient() {
        if (null == httpclient) {
            init();
        }
        return httpclient;
    }

    public static String get(String url) {
        HttpGet httpGet = null;
        String uri = "";
        long start = System.currentTimeMillis();
        try {
            httpGet = new HttpGet(url);
            logger.info("httputil4 get {}", uri);
            CloseableHttpResponse response = httpclient.execute(httpGet);
            int status = response.getStatusLine().getStatusCode();
            if (status == 200) {
                String result = EntityUtils.toString(response.getEntity(), Consts.UTF_8);
                logger.info("httputil4 get {} , result {}", uri, result);
                return result;
            }
            logger.error("httputil4 get response status code not 200 {}, {}", status, uri);
        } catch (Exception e) {
            logger.error("httputil4 get exception {} {}", url, uri, e);
        } finally {
            long cost = System.currentTimeMillis() - start;
            logger.info("httpUtil4_get_cost {}", cost);
            if (null != httpGet)
                httpGet.releaseConnection();
        }
        return null;

    }


    public static String postJsonSimpleResult(String url, String body) {
        return postJsonWithWrapperReuslt(url, body).result;
    }

    public static HttpResultWrapper postJsonWithWrapperReuslt(String url, String body) {
        HttpPost httpPost = null;
        long start = System.currentTimeMillis();
        try {
            httpPost = new HttpPost(url);
            logger.info("request is {}  url is {}", body, url);
            HttpEntity httpEntity = new StringEntity(body, ContentType.APPLICATION_JSON);
            httpPost.setEntity(httpEntity);
            httpPost.setHeader("Accept", "application/json");
            int code = 0;
            CloseableHttpResponse response = httpclient.execute(httpPost);
            code = response.getStatusLine().getStatusCode();
            if (code == 200) {
                HttpEntity responseEntity = response.getEntity();
                String result = EntityUtils.toString(responseEntity, Consts.UTF_8);
                logger.info("request is {}  url is {} response {}", body, url, result);
                return new HttpResultWrapper(code, result);
            }
            logger.error("http postJsonWithWrapperReuslt response status not 200 {}, url {}, body {}", code, url, body);
        } catch (IOException e) {
            logger.error("httpUitl4 push fail {} {} ", url, body, e);
        } finally {
            long cost = System.currentTimeMillis() - start;
            logger.info("httpUtil4_postJson {}", cost);
            if (null != httpPost) {
                httpPost.releaseConnection();
            }
        }
        return new HttpResultWrapper(0, "");
    }


    public static class HttpResultWrapper {
        public int code;
        public String result;

        public HttpResultWrapper(int code, String result) {
            this.code = code;
            this.result = result;
        }
    }

}
